import { viewSize } from '@/scripts/constant';
import extentDOM from '@/scripts/helpers/extentDOM';
import { stbKeys } from '@/scripts/helpers/keyCode';
import '@/assets/css/animate.css';

/** @type {import('node_modules/vue/types/options').ComponentOptions} */
const component = {
  name: 'SnelPage',
  props: {
    // 渲染标签
    tag: { type: String, default: 'div' },
    // 配置
    config: { type: Object, default: () => { return {}; } }
  },
  computed: {
    // 区域控制器数量
    total() {
      const me = this;
      return me.areas.length;
    },
    // 焦点区域控制器对象
    focusArea() {
      const me = this;
      return me.areas[me.currentAreaId];
    },
  },
  data() {
    this.values = null;
    return {
      // 组件标识
      $__sign__: '__snel-page__',
      // 滑动节流timer
      throttleTimer: null,
      // 首次加载标识
      firstLoad: true,
      // 当前区域控制器id
      currentAreaId: +this.config.areaId || 0,
      // 当前区域控制器索引
      currentIndex: +this.config.areaIndex || 0,
      // 移动锁
      moveLock: false,
      // 页面滑动容器
      slideContainer: null,
      // 页面容器滑动量
      translate: null,
      // 区域控制器集合
      areas: [],
    };
  },
  render(h) {
    return h(this.tag, this.$slots.default);
  },
  mounted() {
    const me = this;
    me.$planAreas();
    // 页面容器滑动
    me.$containerSlide();
    // 全局keydown事件处理
    document.addEventListener('keydown', me.$keyDown);
    // 开发环境补充事件
    if (process.env.NODE_ENV === 'development') {
      document.addEventListener('wheel', me.$wheel);
    }
  },
  // 释放资源
  destroyed() {
    const me = this;
    document.removeEventListener('keydown', me.$keyDown);
    // 开发环境补充事件
    if (process.env.NODE_ENV === 'development') {
      document.removeEventListener('wheel', me.$wheel);
    }
  },
  /** @type {EpgPageVueMethod} */
  methods: {
    $wheel(e) {
      this.focusArea?.[`$move${e.deltaY > 0 ? 'Down' : 'Up'}`]();
    },
    $keyDown(e) {
      const keyCode = e.keyCode || e.which;
      this.handleEventByCode(keyCode, e);
    },
    $wrap(context, name, ...args) {
      if (context) {
        const fn = context.config[name];
        fn && fn(context, ...args);
      }
    },
    $scanAreas(children, recursion) {
      if (children?.length) {
        const me = this;
        children.forEach((child) => {
          if (recursion) {// 递归查找符合配置标识的组件
            if (child.$parent.$attrs.sign === me.$attrs.sign) me.areas.push(child);
          } else {
            if (child.$data.$__sign__ === '__snel-area__') {
              me.areas.push(child);
            } else me.$scanAreas(child.$children, true);
          }
        });
      }
    },
    $planAreas() {
      const me = this;
      me.areas = [];
      me.$scanAreas(me.$children);
      if (me.total) {
        me.areas.forEach((area, i) => {
          // 初始化区域控制器组件id
          area.id = i;
          // 指向当前页面控制器
          area.page = me;
          // 初始化驻留
          const index = area.config.initRecord;
          if (undefined !== index) {
            area.currentIndex = index;
            area.id !== me.currentAreaId ? area.switchSelect(index) : area.recordIndex = index;
          }
          // 容器滑动
          area.$containerSlide();
        });
      }
    },
    $slidePrepare() {
      const me = this;
      if (me.areas.length) {
        const slide = me.config.slide;
        const maxHeight = viewSize.height;
        const maxMiddle = maxHeight / 2;
        const bottom = slide.bottom || 50;
        const lastArea = me.areas.slice(-1)[0];
        const lastAreaRect = lastArea?.containerRect;
        const min = Math.floor(maxHeight - lastAreaRect?.bottom - bottom);
        return me.areas.map(area => {
          const rect = area.containerRect;
          const height = rect.height;
          const middle = height / 2;
          if (rect.bottom > maxHeight) {
            const value = Math.floor(maxMiddle - (rect.top + middle));
            return value < min ? min : value;
          }
          return 0;
        });
      }
    },
    $containerSlide() {
      const me = this;
      const slide = me.config.slide;
      const container = me.slideContainer;
      if (!slide || !me.slideContainer) return;
      const id = me.currentAreaId;
      const val = me.values?.[id];
      if (undefined !== val) {
        if (container) {
          if (me.translate !== val) {
            me.translate = val;
            if (slide.condition) {
              const move = slide.condition(me, val);
              if (!move) return;
            }
            // 滑动锁定
            // if (!me.firstLoad && !slide.slideFree) me.moveLock = true;
            const dir = slide.slideDir || 'v';
            me.moveLock = true;
            container.slide(val, dir, me.firstLoad ? 0 : slide.transition);
            if (!me.throttleTimer) {
              me.throttleTimer = window.setTimeout(() => {
                window.clearTimeout(me.throttleTimer), me.throttleTimer = 0, me.moveLock = false;
              }, Math.floor((slide.transition || 300) / 2));
            }
          }
        }
      }
    },
    $changeScreen(step, speed) {
      const me = this;
      /** @type {EpgAreaVueMethod} */
      const focusArea = me.focusArea;
      if (focusArea.curDom) focusArea.curDom.className = focusArea.config.itemClass || 'item';
      focusArea.marqueeStop();
      const slideDir = me.config.screen.slideDir;
      const area = me.areas[me.config.screen.dependId];
      let index = area.currentIndex;
      index += step;
      index < 0 ? me.total - 1 : index;
      index > me.total - 1 ? 0 : index;
      const distance = -index * (slideDir === 'h' ? viewSize.width : viewSize.height);
      me.slideContainer.slide(0, slideDir === 'h' ? 'v' : 'h', 0);
      const t = window.setTimeout(() => {
        window.clearTimeout(t);
        me.slideContainer.slide(distance, slideDir, speed);
      }, 0);
      area.switchSelect(index);
      return index;
    },
    prevScreen(speed) {
      const me = this;
      return me.$changeScreen(-1, speed);
    },
    nextScreen(speed) {
      const me = this;
      return me.$changeScreen(1, speed);
    },
    handleEventByCode(keyCode, e) {
      const me = this;
      /** @type {EpgAreaVueMethod} */
      const focusArea = me.focusArea;
      me.$wrap(me, 'getKeyCode', keyCode);
      switch (keyCode) {
        case stbKeys.KEY_0: case stbKeys.KEY_1: case stbKeys.KEY_2:
        case stbKeys.KEY_3: case stbKeys.KEY_4: case stbKeys.KEY_5:
        case stbKeys.KEY_6: case stbKeys.KEY_7: case stbKeys.KEY_8:
        case stbKeys.KEY_9:
          const n = ~~keyCode - stbKeys.KEY_0; me.$wrap(me, 'keyNumber', n);
          break;
        case stbKeys.KEY_N0: case stbKeys.KEY_N1: case stbKeys.KEY_N2:
        case stbKeys.KEY_N3: case stbKeys.KEY_N4: case stbKeys.KEY_N5:
        case stbKeys.KEY_N6: case stbKeys.KEY_N7: case stbKeys.KEY_N8:
        case stbKeys.KEY_N9:
          const nn = ~~keyCode - stbKeys.KEY_N0; me.$wrap(me, 'keyNumber', nn);
          break;
        case stbKeys.KEY_UP: case stbKeys.KEY_W: !me.moveLock && focusArea?.$moveUp(); break;
        case stbKeys.KEY_DOWN: case stbKeys.KEY_S: !me.moveLock && focusArea?.$moveDown(); break;
        case stbKeys.KEY_LEFT: case stbKeys.KEY_A: !me.moveLock && focusArea?.$moveLeft(); break;
        case stbKeys.KEY_RIGHT: case stbKeys.KEY_D: !me.moveLock && focusArea?.$moveRight(); break;
        case stbKeys.KEY_OK: me.$wrap(me, 'keyOk'), me.$wrap(focusArea, 'keyOk'); break;
        case stbKeys.KEY_PAGE_UP:
          me.$wrap(me, 'keyPrevPage');
          if (focusArea.curPage > 1) {
            focusArea.curPage--;
            me.$wrap(focusArea, 'keyPrevPage');
          }
          break;
        case stbKeys.KEY_PAGE_DOWN:
          me.$wrap(me, 'keyNextPage');
          if (focusArea.curPage < focusArea.totalPage) {
            focusArea.curPage++;
            me.$wrap(focusArea, 'keyPrevPage');
          }
          break;
        case 32: // pc
        case stbKeys.KEY_keyBack: me.$wrap(me, 'keyBack'); break;
        case stbKeys.KEY_VOL_UP: me.$wrap(me, 'keyVolumeUp'); break;
        case stbKeys.KEY_VOL_DOWN: me.$wrap(me, 'keyVolumeDown'); break;
        case stbKeys.KEY_MUTE: me.$wrap(me, 'keyVolumeMute'); break;
        case stbKeys.KEY_HOME: me.$wrap(me, 'keyHome'); break;
        case 90: // z测试
        case 272: // 烽火
        case stbKeys.KEY_MENU: me.$wrap(me, 'keyMenu'); break;
        case stbKeys.KEY_CHANNEL_UP: me.$wrap(me, 'keyNextChannel'); break;
        case stbKeys.KEY_CHANNEL_DOWN: me.$wrap(me, 'keyPrevChannel'); break;
        case stbKeys.KEY_FAST_FORWARD: me.$wrap(me, 'keyFastForward'); break;
        case stbKeys.KEY_FAST_REWIND: me.$wrap(me, 'keyFastRewind'); break;
        case stbKeys.KEY_PAUSE: me.$wrap(me, 'keyPause'); break;
        case 150: // 移动收藏
        case stbKeys.KEY_COLLECT: me.$wrap(me, 'keyCollect'); break;
        case stbKeys.KEY_STOP: me.$wrap(me, 'keyStop'); break;
        case stbKeys.KEY_SEARCH: me.$wrap(me, 'keySearch'); break;
        case stbKeys.KEY_DEL: me.$wrap(me, 'keyDel'); break;
        case stbKeys.KEY_APP: me.$wrap(me, 'keyApp'); break;
        case stbKeys.KEY_LIVE: me.$wrap(me, 'keyLive'); break; // 直播
        case stbKeys.KEY_TVOD: me.$wrap(me, 'keyTvod'); break; // 回看
        case stbKeys.KEY_VOD: me.$wrap(me, 'keyVod'); break; // 点播
        case stbKeys.KEY_INFO: me.$wrap(me, 'keyInfo'); break; // 信息
        case stbKeys.KEY_VIRTUAL: me.$wrap(me, 'keyVirtual', e); break;
        default: me.$wrap(me, 'unhandledEvents', e); break;
      }
    },
    changeFocus(id, index) {
      const me = this;
      /** @type {EpgAreaVueMethod} */
      const oldArea = me.focusArea;
      const self = id === me.currentAreaId;// 目标是当前区域
      if (oldArea && !me.firstLoad) {
        // 区域失去焦点前事件
        self && me.$wrap(oldArea, 'blurBefore');
        const dom = oldArea.curDom;
        if (oldArea.id !== id) {
          const outReset = oldArea.config.slide?.outReset;
          outReset && oldArea.resetSlide(0);
        }
        const bannedShake = oldArea.config.bannedShake;
        const pBorderShake = me.config.borderShake;
        if (!bannedShake && pBorderShake) {
          const { banned = [], className: name } = pBorderShake;
          if (!banned.includes(oldArea.id)) {
            if (dom.className.includes(name || 'shake-item-')) {
              dom.className = oldArea.config.itemClass || 'item';
            }
          }
        }
        // 区域元素失去焦点
        if (dom) dom.className = oldArea.config.blurClass || 'item';
        // 停止文字滚动
        oldArea.marqueeStop();
        // 区域失去焦点后事件
        self && me.$wrap(oldArea, 'blurAfter');
      }
      if (undefined === id || typeof +id !== 'number') id = me.currentAreaId;
      const maxId = me.total - 1;
      me.currentAreaId = id > maxId ? maxId : id;
      /** @type {EpgAreaVueMethod} */
      const focusArea = me.focusArea;
      if (focusArea) {
        if (undefined === index || typeof +index !== 'number') {
          if (!me.firstLoad && focusArea.config.record) {
            focusArea.clearSelect();
            index = focusArea.recordIndex;
          } else index = me.currentIndex;
        }
        // 区域获取焦点前事件
        self && me.$wrap(focusArea, 'focusBefore');
        me.currentIndex = index;
        // 更新区域索引
        focusArea.currentIndex = me.currentIndex;
        // 页面容器滑动
        me.$containerSlide();
        // 区域容器滑动
        focusArea.$containerSlide();
        // 更新区域分页数据
        focusArea.$setPagenation();
        // 焦点区域元素获取焦点
        const dom = focusArea.curDom;
        if (dom) dom.className = focusArea.config.focusClass || 'item item_focus';
        // 区域获取焦点后事件
        self && me.$wrap(focusArea, 'focusAfter');
        // 获取焦点后事件
        me.$wrap(me, 'focusListener', focusArea);
        // 启动文字滚动
        if (undefined !== focusArea.config.bannedMarquee) {
          !focusArea.config.bannedMarquee && focusArea.marqueeMove();
        } else {
          if (!me.config.bannedMarquee || !me.config.bannedMarquee?.includes(focusArea.id)) {
            focusArea.marqueeMove();
          }
        }
      }
    },
    init(areaId, areaIndex) {
      const me = this;
      me.$nextTick(() => {
        me.$planAreas();
        const slide = me.config.slide;
        if (slide?.containerId) {
          me.slideContainer = extentDOM(document.getElementById(slide.containerId));
        }
        if (!slide?.values) {// 没有配置滑动量
          me.values = me.$slidePrepare();
        } else me.values = slide.values;
        // 加入队列延后执行, 保证该值对外的正确性
        const t = window.setTimeout(() => {
          window.clearTimeout(t);
          me.changeFocus(areaId, areaIndex);
          me.firstLoad = false;
        }, 0);
      });
    },
  },
};

export default component;
